﻿using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;

namespace System.Windows.Forms
{
	public static class ExtensionMethods
	{
		#region SyncWithUI
		/// <summary>
		/// Used to easily run code that must be invoked on the UI's 
		/// message pump thread.  Commonly used for binding data to
		/// the UI from an async database operation.
		/// </summary>
		/// <param name="controlToInvokeOn"></param>
		/// <param name="code"></param>
		public static void SyncWithUI(this Control controlToInvokeOn, Action code)
		{
			if (controlToInvokeOn.InvokeRequired)
			{
				controlToInvokeOn.Invoke(code);
			}
			else
			{
				code();
			}
		}

		/// <summary>
		/// Used to easily run code that must be invoked on the UI's 
		/// message pump thread.  Commonly used for binding data to
		/// the UI from an async database operation.
		/// 
		/// The overload can be used to directly extract the values from 
		/// a UI element into an async context. IE:
		/// <code>
		/// // in non-UI context, this = Form control
		/// 
		/// string filter = this.SyncWithUI(()=> FilterTextBox.Text);
		/// </code>
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="controlToInvokeOn"></param>
		/// <param name="code"></param>
		/// <returns></returns>
		public static T SyncWithUI<T>(this Control controlToInvokeOn, Func<T> code)
		{
			T t;
			if (controlToInvokeOn.InvokeRequired)
			{
				t = (T)controlToInvokeOn.Invoke(code);
			}
			else
			{
				t = code();
			}

			return t;
		}

		public static void ForceSyncWithUI(this Control controlToInvokeOn, Action code)
		{
			controlToInvokeOn.Invoke(code);
		}
		#endregion
		public static void DragForm(this Form form)
		{
			HandleRef windowHandle = new System.Runtime.InteropServices.HandleRef(form, form.Handle);
			NativeMethods.ReleaseCapture();
			NativeMethods.SendMessage(windowHandle,
				NativeMethods.WM_NCLBUTTONDOWN, (IntPtr)NativeMethods.HTCAPTION, (IntPtr)0);
		}

		public static byte[] ReadToEnd(this Stream stream, bool closeStream, int bufferSize)
		{
			using (MemoryStream writeStream =
				stream.CanSeek ?
				new MemoryStream((int)stream.Length) :
				new MemoryStream())
			{

				byte[] buffer = new byte[bufferSize];

				int bytesRead = stream.Read(buffer, 0, bufferSize);

				while (bytesRead > 0)
				{
					writeStream.Write(buffer, 0, bytesRead);
					bytesRead = stream.Read(buffer, 0, bufferSize);
				}

				if (closeStream)
				{
					stream.Close();
				}

				return writeStream.ToArray();
			}
		}

		public static byte[] ReadToEnd(this Stream stream)
		{
			return ReadToEnd(stream, true, 4096);
		}

		public static byte[] ReadToEnd(this Stream stream, bool closeStream)
		{
			return ReadToEnd(stream, closeStream, 4096);
		}

		public static bool HitTest(this MouseEventArgs args, Rectangle hitArea)
		{
			if (args.X >= hitArea.X &&
				args.Y >= hitArea.Y &&
				args.X <= (hitArea.X + hitArea.Width) &&
				args.Y <= (hitArea.Y + hitArea.Height))
			{
				return true;
			}

			return false;
		}

		public static void Minimize(this Form form)
		{
			NativeMethods.ShowWindowAsync(form.Handle, NativeMethods.SW_SHOWMINIMIZED);
		}

		public static void Maximize(this Form form)
		{
			NativeMethods.ShowWindowAsync(form.Handle, NativeMethods.SW_SHOWMAXIMIZED);
		}

		public static void Restore(this Form form)
		{
			NativeMethods.ShowWindowAsync(form.Handle, NativeMethods.SW_SHOWNORMAL);
		}
	}

	#region NativeMethods
	static class NativeMethods
	{
		public const uint WM_NCLBUTTONDOWN = 0xA1;
		public const int HTCAPTION = 0x2;

		[SuppressUnmanagedCodeSecurityAttribute()]
		[DllImport("user32", CharSet = CharSet.Auto, SetLastError = false)]
		internal static extern IntPtr SendMessage(HandleRef hwnd, uint wMsg, IntPtr wParam, IntPtr lParam);

		//[SuppressUnmanagedCodeSecurityAttribute()]
		//[DllImport("user32.dll")]
		//public static extern uint SendMessage(IntPtr hWnd, uint msg, uint wParam, uint lParam);

		[SuppressUnmanagedCodeSecurityAttribute()]
		[DllImport("user32")]
		[return: MarshalAs(UnmanagedType.Bool)]
		internal static extern bool ReleaseCapture();

		#region GetForegroundWindow
		[SuppressUnmanagedCodeSecurityAttribute()]
		[DllImport("user32.dll")]
		internal static extern IntPtr GetForegroundWindow();
		#endregion

		#region FlashWindow
		//Stop flashing. The system restores the window to its original state. 
		public const UInt32 FLASHW_STOP = 0;
		//Flash the window caption. 
		public const UInt32 FLASHW_CAPTION = 1;
		//Flash the taskbar button. 
		public const UInt32 FLASHW_TRAY = 2;

		//Flash both the window caption and taskbar button.
		//This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags. 
		public const UInt32 FLASHW_ALL = 3;
		//Flash continuously, until the FLASHW_STOP flag is set. 
		public const UInt32 FLASHW_TIMER = 4;
		//Flash continuously until the window comes to the foreground. 
		public const UInt32 FLASHW_TIMERNOFG = 12;

		[SuppressUnmanagedCodeSecurityAttribute()]
		[DllImport("user32.dll")]
		static extern Int32 FlashWindowEx(ref FLASHWINFO pwfi);

		public static bool StopFlashWindow(IntPtr hWnd)
		{
			FLASHWINFO fInfo = new FLASHWINFO();

			fInfo.cbSize = Convert.ToUInt32(Marshal.SizeOf(fInfo));
			fInfo.hwnd = hWnd;
			fInfo.dwFlags = FLASHW_STOP;
			fInfo.uCount = UInt32.MaxValue;
			fInfo.dwTimeout = 0;

			return (FlashWindowEx(ref fInfo) == 0);
		}

		public static bool FlashWindow(IntPtr hWnd)
		{
			FLASHWINFO fInfo = new FLASHWINFO();

			fInfo.cbSize = Convert.ToUInt32(Marshal.SizeOf(fInfo));
			fInfo.hwnd = hWnd;
			fInfo.dwFlags = FLASHW_TIMERNOFG | FLASHW_TRAY;
			fInfo.uCount = UInt32.MaxValue;
			fInfo.dwTimeout = 0;

			return (FlashWindowEx(ref fInfo) == 0);
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct FLASHWINFO
		{
			public UInt32 cbSize;
			public IntPtr hwnd;
			public UInt32 dwFlags;
			public UInt32 uCount;
			public UInt32 dwTimeout;
		}

		#endregion

		#region ShowWindowAsync
		public const int SW_SHOWNORMAL = 1;
		public const int SW_SHOWMINIMIZED = 2;
		public const int SW_SHOWMAXIMIZED = 3;

		[DllImport("user32.dll")]
		public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

		#endregion
	}
	#endregion
}
